#include "widgetboxtreewidget.h"



#include "sheetdelegate.h"
#include "widgetboxcategorylistview.h"
#include "qwidgetfactory.h"
#include "qdesignermimedata.h"

#include "../../lib/shared/qwidgetfactory.h"

#include <QFile>
#include <QApplication>
#include <QHeaderView>

#include <QXmlStreamReader>
#include <QActionGroup>
#include <QMenu>
#include <QContextMenuEvent>
#include <QIcon>
#include <QResizeEvent>


enum TopLevelRole  { NORMAL_ITEM, SCRATCHPAD_ITEM, CUSTOM_ITEM };

static TopLevelRole topLevelRole(const  QTreeWidgetItem *item)
{
    return static_cast<TopLevelRole>(item->data(0, Qt::UserRole).toInt());
}

static void setTopLevelRole(TopLevelRole tlr, QTreeWidgetItem *item)
{
    item->setData(0, Qt::UserRole, QVariant(tlr));
}

WidgetBoxTreeWidget::WidgetBoxTreeWidget(QWidget *parent) :
    QTreeWidget(parent),
    m_iconMode(false)
{
    setFocusPolicy(Qt::NoFocus);
    setIndentation(0);
    setRootIsDecorated(false);
    setColumnCount(1);
    header()->hide();
    header()->setResizeMode(QHeaderView::Stretch);
    setTextElideMode(Qt::ElideMiddle);
    setVerticalScrollMode(ScrollPerPixel);
    setItemDelegate(new SheetDelegate(this, this));

    connect(this, SIGNAL(itemPressed(QTreeWidgetItem*,int)),
            this, SLOT(handleMousePress(QTreeWidgetItem*)));
    connect(this,SIGNAL(pressed(QString,QString,QPoint)),this,SLOT(handleMousePress(QString,QString,QPoint)));

    this->setFrameStyle(QFrame::NoFrame);
    this->setAcceptDrops(true);

    QFile f(":/widgetbox.xml");
    if(f.open(QFile::ReadOnly))
    {
        QString xml=f.readAll();
        QXmlStreamReader r(xml);


        while(!r.atEnd())
        {
            if(r.readNext()==QXmlStreamReader::StartElement)
            {
                const QStringRef tag = r.name();
                if(tag.toString()=="widgetbox")
                {
                    continue;
                }
                else if(tag.toString()=="category")
                {
                    readCategory(xml,r);
                }
            }
        }
    }

    QList<QWidgetPluginInterface*> interfaces=WidgetFactory::interface();

    foreach(QWidgetPluginInterface *interface,interfaces)
    {
        QStringList list=interface->keys();
        foreach(QString name,list)
        {
            tagWidgetInfo info=interface->widgetInfo(name);
            Widget w;
            w.setClassName(info.m_class);
            w.setIcon(QIcon(info.m_icon));
            w.setName(info.m_name);
            w.setUI(info.m_ui);
            addWidget(info.m_category,w);
        }
    }
}

WidgetBoxCategoryListView *WidgetBoxTreeWidget::categoryViewAt(int idx) const
{
    WidgetBoxCategoryListView *rc = 0;
    if (QTreeWidgetItem *cat_item = topLevelItem(idx))
        if (QTreeWidgetItem *embedItem = cat_item->child(0))
            rc = qobject_cast<WidgetBoxCategoryListView*>(itemWidget(embedItem, 0));
    Q_ASSERT(rc);
    return rc;
}

WidgetBoxTreeWidget::~WidgetBoxTreeWidget()
{
}

void WidgetBoxTreeWidget::handleMousePress(QTreeWidgetItem *item)
{
    if (item == 0)
        return;

    if (QApplication::mouseButtons() != Qt::LeftButton)
        return;

    if (item->parent() == 0) {
        setItemExpanded(item, !isItemExpanded(item));
        return;
    }
}

void WidgetBoxTreeWidget::handleMousePress(QString ui, QString c, QPoint pt)
{
    if (QApplication::mouseButtons() != Qt::LeftButton)
        return;
    QList<QDesignerDnDItemInterface*> item_list;
    item_list.append(new WidgetBoxDnDItem(c,ui,pt));

    foreach(QDesignerDnDItemInterface *item,item_list)
    {
        if(item->widget()==NULL)
        {
            return;
        }
    }

    QDesignerMimeData::execDrag(item_list,this);
}

int WidgetBoxTreeWidget::indexOfCategory(const QString &name) const
{
    const int topLevelCount = topLevelItemCount();
    for (int i = 0; i < topLevelCount; ++i) {
        if (topLevelItem(i)->text(0) == name)
            return i;
    }
    return -1;
}

void WidgetBoxTreeWidget::adjustSubListSize(QTreeWidgetItem *cat_item)
{
    QTreeWidgetItem *embedItem = cat_item->child(0);
    if (embedItem == 0)
        return;

    WidgetBoxCategoryListView *list_widget = static_cast<WidgetBoxCategoryListView*>(itemWidget(embedItem, 0));
    list_widget->setFixedWidth(header()->width());
    list_widget->doItemsLayout();
    const int height = qMax(list_widget->contentsSize().height() ,1);
    list_widget->setFixedHeight(height);
    embedItem->setSizeHint(0, QSize(-1, height - 1));
}

int WidgetBoxTreeWidget::categoryCount() const
{
    return topLevelItemCount();
}

Category WidgetBoxTreeWidget::category(int cat_idx) const
{
    if (cat_idx >= topLevelItemCount())
        return Category();

    QTreeWidgetItem *cat_item = topLevelItem(cat_idx);

    QTreeWidgetItem *embedItem = cat_item->child(0);
    WidgetBoxCategoryListView *categoryView = static_cast<WidgetBoxCategoryListView*>(itemWidget(embedItem, 0));

    Category result = categoryView->category();
    result.setName(cat_item->text(0));
    return result;
}

int WidgetBoxTreeWidget::widgetCount(int cat_idx) const
{
    if (cat_idx >= topLevelItemCount())
        return 0;
    return categoryViewAt(cat_idx)->count();
}

Widget WidgetBoxTreeWidget::widget(int cat_idx, int wgt_idx) const
{
    if (cat_idx >= topLevelItemCount())
        return Widget();
    // SDK functions want unfiltered access
    WidgetBoxCategoryListView *categoryView = categoryViewAt(cat_idx);
    return categoryView->widgetAt(wgt_idx);
}

void WidgetBoxTreeWidget::addWidget(QString name, const Widget &wgt)
{
    int index=indexOfCategory(name);
    WidgetBoxCategoryListView *categoryView;
    QTreeWidgetItem *cat_item;
    if (index<0)
    {
        cat_item = new QTreeWidgetItem();
        cat_item->setText(0, name);
        setTopLevelRole(NORMAL_ITEM, cat_item);
        addTopLevelItem(cat_item);
        setItemExpanded(cat_item, true);
        cat_item->setFlags(Qt::ItemIsEnabled);
        QTreeWidgetItem *embed_item=new QTreeWidgetItem(cat_item);
        categoryView = new WidgetBoxCategoryListView(this);
        categoryView->setViewMode(m_iconMode ? QListView::IconMode : QListView::ListMode);
        connect(categoryView, SIGNAL(pressed(QString,QString,QPoint)), this, SIGNAL(pressed(QString,QString,QPoint)));
        setItemWidget(embed_item, 0, categoryView);
    }
    else
    {
        cat_item=topLevelItem(index);
        categoryView=categoryViewAt(index);
    }

    if(categoryView==NULL || cat_item==NULL)
    {
        return;
    }

    categoryView->addWidget(wgt,wgt.icon(),false);
    adjustSubListSize(cat_item);
}

void WidgetBoxTreeWidget::slotListMode()
{
    m_iconMode = false;
    updateViewMode();
}

void WidgetBoxTreeWidget::slotIconMode()
{
    m_iconMode = true;
    updateViewMode();
}

void WidgetBoxTreeWidget::updateViewMode()
{
    if (const int numTopLevels = topLevelItemCount()) {
        for (int i = numTopLevels - 1; i >= 0; --i) {
            QTreeWidgetItem *topLevel = topLevelItem(i);
            const QListView::ViewMode viewMode  = m_iconMode && (topLevelRole(topLevel) != SCRATCHPAD_ITEM) ? QListView::IconMode : QListView::ListMode;
            WidgetBoxCategoryListView *categoryView = categoryViewAt(i);
            if (viewMode != categoryView->viewMode()) {
                categoryView->setViewMode(viewMode);
                adjustSubListSize(topLevelItem(i));
            }
        }
    }

    updateGeometries();
}

void WidgetBoxTreeWidget::resizeEvent(QResizeEvent *e)
{
    QTreeWidget::resizeEvent(e);
    if (const int numTopLevels = topLevelItemCount()) {
        for (int i = numTopLevels - 1; i >= 0; --i)
            adjustSubListSize(topLevelItem(i));
    }
}

void WidgetBoxTreeWidget::contextMenuEvent(QContextMenuEvent *e)
{
    QMenu menu;
    menu.addAction(tr("Expand all"), this, SLOT(expandAll()));
    menu.addAction(tr("Collapse all"), this, SLOT(collapseAll()));
    menu.addSeparator();

    QAction *listModeAction = menu.addAction(tr("List View"));
    QAction *iconModeAction = menu.addAction(tr("Icon View"));
    listModeAction->setCheckable(true);
    iconModeAction->setCheckable(true);
    QActionGroup *viewModeGroup = new QActionGroup(&menu);
    viewModeGroup->addAction(listModeAction);
    viewModeGroup->addAction(iconModeAction);
    if (m_iconMode)
        iconModeAction->setChecked(true);
    else
        listModeAction->setChecked(true);
    connect(listModeAction, SIGNAL(triggered()), SLOT(slotListMode()));
    connect(iconModeAction, SIGNAL(triggered()), SLOT(slotIconMode()));

    e->accept();
    menu.exec(mapToGlobal(e->pos()));
}

void WidgetBoxTreeWidget::readCategory(const QString &xml, QXmlStreamReader &read)
{
    QString categoryName;
    foreach(const QXmlStreamAttribute &attribute,read.attributes())
    {
        QStringRef name=attribute.name();
        if(name==QLatin1String("name"))
        {
            categoryName=attribute.value().toString();
        }
    }
    for(bool finished=false;!finished && !read.hasError();)
    {
        switch(read.readNext())
        {
        case QXmlStreamReader::StartElement:
            readCategoryentry(xml,categoryName,read);
            break;
        case QXmlStreamReader::EndElement:
            finished=true;
            break;
        default:
            break;
        }
    }
}

void WidgetBoxTreeWidget::readCategoryentry(const QString &xml, const QString &category, QXmlStreamReader &read)
{
    Widget wid;
    foreach(const QXmlStreamAttribute &attribute,read.attributes())
    {
        QStringRef name=attribute.name();
        if(name==QLatin1String("name"))
        {
            wid.setName(attribute.value().toString());
        }
        else if(name==QLatin1String("icon"))
        {
            wid.setIcon(QIcon(":/images/"+attribute.value().toString()));
        }
    }

    int c=0;
    int begin=0;
    int end=0;
    for(bool finished=false;!finished && !read.hasError();)
    {
        switch(read.readNext())
        {
        case QXmlStreamReader::StartElement:
            if(c==0)
            {
                begin=read.characterOffset();
            }
            c++;
            break;
        case QXmlStreamReader::EndElement:
            c--;
            if(c==-1)
            {
                finished=true;
            }
            if(c==1)
            {
                end=read.characterOffset();
            }
            break;
        default:
            break;
        }
    }
    QString ui=xml.mid(begin,end-begin);
    wid.setUI(ui);
    addWidget(category,wid);
}
